package com.purvar.mybatis.util.mapper;

import com.baomidou.mybatisplus.annotation.TableId;
import com.purvar.exception.BusinessException;
import com.purvar.mybatis.util.anotation.*;
import com.purvar.mybatis.util.bean.InverseJoinColumnen;
import com.purvar.mybatis.util.bean.JoinColumnen;
import com.purvar.mybatis.util.bean.JoinTableEn;
import com.purvar.mybatis.util.bean.Relation;
import com.purvar.mybatis.util.enums.FieldCollectionType;
import com.purvar.mybatis.util.enums.RelationType;
import lombok.Data;
import org.springframework.util.StringUtils;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.*;

/**
 * AutoMapper for one2one/one2many/many2many
 *
 * @author zcc
 */
@Data
public class AutoMapper extends AbstractAutoMapper {
    private String[] entityPackages;


    public AutoMapper() {
        this.entityPackages = new String[]{};
    }

    public void initData(String[] entityPackages) {
        if (entityPackages != null && entityPackages.length > 0) {
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            Class<?> clazz = null;
            String className = null;
            for (int i = 0; i < entityPackages.length; i++) {
                String entityPackage = entityPackages[i];
                String packagePath = entityPackage.replace(".", "/");

                URL url = loader.getResource(packagePath);
                if (url != null) {
                    String protocol = url.getProtocol();
                    if (protocol.equals("file")) {
                        File file = new File(url.getPath());
                        File[] files = file.listFiles();
                        for (File childFile : files) {
                            String fileName = childFile.getName();
                            if (fileName.endsWith(".class") && !fileName.contains("$")) {
                                className = entityPackage + "." + fileName.substring(0, fileName.length() - 6);

                                List<Relation> relations = new ArrayList<>();
                                try {
                                    clazz = Class.forName(className);

                                    Field[] fields = clazz.getDeclaredFields();
                                    if (fields != null && fields.length > 0) {
                                        for (int j = 0; j < fields.length; j++) {
                                            Field field = fields[j];
                                            OneToOne oneToOne = field.getAnnotation(OneToOne.class);

                                            OneToMany oneToMany = field.getAnnotation(OneToMany.class);

                                            ManyToOne manyToOne = field.getAnnotation(ManyToOne.class);

                                            ManyToMany manyToMany = field.getAnnotation(ManyToMany.class);

                                            Relation relation = new Relation();
                                            TableId tableId = field.getAnnotation(TableId.class);
                                            if (tableId != null) {
                                                relation.setTableId(field.getName());
                                            }

                                            if (oneToOne != null) {
                                                relation.setRelationType(RelationType.ONETOONE);
                                                if (oneToOne.targetEntity() != void.class) {
                                                    relation.setMapperClazz(oneToOne.targetEntity());
                                                }
                                            } else if (oneToMany != null) {
                                                relation.setRelationType(RelationType.ONETOMANY);
                                                if (oneToMany.targetEntity() != void.class) {
                                                    relation.setMapperClazz(oneToMany.targetEntity());
                                                }
                                            } else if (manyToOne != null) {
                                                relation.setRelationType(RelationType.MANYTOONE);
                                                if (manyToOne.targetEntity() != void.class) {
                                                    relation.setMapperClazz(manyToOne.targetEntity());
                                                }
                                            } else if (manyToMany != null) {
                                                relation.setRelationType(RelationType.MANYTOMANY);
                                                if (manyToMany.targetEntity() != void.class) {
                                                    relation.setMapperClazz(manyToMany.targetEntity());
                                                }
                                            } else {
                                                continue;
                                            }
                                            if (relation.getMapperClazz() == null) {
                                                relation.setMapperClazz(getFiledClass(field));
                                            }
                                            JoinTable joinTable = field.getAnnotation(JoinTable.class);

                                            if (manyToMany != null && manyToMany.targetEntity() != void.class) {
                                                initTableFiled(joinTable, relation, className);
                                            }
                                            //初始化其他数据
                                            initDefaultMapper(field, relation, className);

                                            initJionClomns(field, relation);

                                            initInverseJionClomns(field, relation);
                                            //初始化关联关系
                                            relations.add(relation);
                                        }

                                    }

                                } catch (ClassNotFoundException e) {
                                    e.printStackTrace();
                                    throw new BusinessException("Error in scan entity bean");
                                }
                                this.getRelationListMap().put(fileName.substring(0, fileName.length() - 6), relations);
                            }
                        }
                    }
                }

            }
        }
    }
    public AutoMapper(String[] entityPackages) {
        this.entityPackages = entityPackages;


    }

    private void initTableFiled(JoinTable joinTable, Relation relation, String voname) {
        JoinTableEn joinTableEn = new JoinTableEn();
        joinTableEn.setRelationClazz(joinTable.entityClass());
        Class mapperClass = null;
        String entityName = joinTable.entityClass().getSimpleName();
        Collection<Class<?>> mappers = this.factory.getObject().getConfiguration().getMapperRegistry().getMappers();
        for (Class<?> mapperClz : mappers) {
            String mapperClassName = mapperClz.getSimpleName();
            if (mapperClassName.equalsIgnoreCase(entityName + "Mapper")) {
                mapperClass = mapperClz;
                break;
            }
        }
        if (mapperClass == null) {
            throw new BusinessException(voname + "中@JoinTable指定的entityClass非数据库映射字段");
        }
        joinTableEn.setRelationMapper(mapperClass);
        relation.setJoinTableEn(joinTableEn);

    }

    public Class getFiledClass(Field field) {
        boolean isCollection = field.getType() == List.class || field.getType() == ArrayList.class
                || field.getType() == Set.class || field.getType() == HashSet.class;
        Class fieldClass = field.getType();
        if (isCollection) {
            Type genericType = field.getGenericType();
            if (genericType != null && genericType instanceof ParameterizedType) {
                ParameterizedType pt = (ParameterizedType) genericType;
                fieldClass = (Class<?>) pt.getActualTypeArguments()[0];
            }
        }
        return fieldClass;
    }

    public void initDefaultMapper(Field field, Relation relation, String voName) {
        String name = field.getName();
        boolean isCollection = field.getType() == List.class || field.getType() == ArrayList.class
                || field.getType() == Set.class || field.getType() == HashSet.class;
        Class fieldClass = field.getType();
        if (isCollection) {
            Type genericType = field.getGenericType();
            if (genericType != null && genericType instanceof ParameterizedType) {
                ParameterizedType pt = (ParameterizedType) genericType;
                fieldClass = (Class<?>) pt.getActualTypeArguments()[0];
            }
        }
        //设置属性名称
        relation.setFiledName(name);
        //设置属性类型,集合还是普通类
        if (field.getType() == List.class || field.getType() == ArrayList.class) {
            relation.setFieldCollectionType(FieldCollectionType.LIST);
        } else if (field.getType() == Set.class || field.getType() == HashSet.class) {
            relation.setFieldCollectionType(FieldCollectionType.SET);
        } else {
            relation.setFieldCollectionType(FieldCollectionType.NONE);
        }
        //设置数据映射的实体
        relation.setEntityClazz(fieldClass);
        //设置mapper
        Class mapperClass = null;
        String entityName = relation.getMapperClazz() != null ? relation.getMapperClazz().getSimpleName() : fieldClass.getSimpleName();
        Collection<Class<?>> mappers = this.factory.getObject().getConfiguration().getMapperRegistry().getMappers();
        for (Class<?> mapperClz : mappers) {
            String mapperClassName = mapperClz.getSimpleName();
            if (mapperClassName.equalsIgnoreCase(entityName + "Mapper")) {
                mapperClass = mapperClz;
                break;
            }
        }

        if (mapperClass == null) {
            throw new BusinessException(
                    "[Class:初始化" + voName + "对象出错,请指定" + voName + "中" + "注解" + relation.getRelationType().name() + "的targetEntity值为数据库对象");
        }
        relation.setMapper(mapperClass);


    }


    public void initJionClomns(Field field, Relation relation) {
        JoinColumn joinColumn = field.getAnnotation(JoinColumn.class);
        JoinColumns joinColumns = field.getAnnotation(JoinColumns.class);

        List<JoinColumnen> joinColumnens = new ArrayList<>();
        if (joinColumn != null) {
            JoinColumnen joinColumnen = new JoinColumnen();
            joinColumnen.setName(joinColumn.name());
            if (StringUtils.isEmpty(joinColumn.referencedColumnName())) {
                joinColumnen.setReferencedColumnName(HumpToUnderline(relation.getTableId()));

            } else joinColumnen.setReferencedColumnName(joinColumn.referencedColumnName());
            joinColumnens.add(joinColumnen);
        }
        if (joinColumns != null) {
            for (int i = 0; i < joinColumns.value().length; i++) {
                JoinColumnen joinColumnen = new JoinColumnen();
                joinColumnen.setName(joinColumns.value()[i].name());
                joinColumnen.setReferencedColumnName(joinColumns.value()[i].referencedColumnName());
                joinColumnens.add(joinColumnen);
            }
        }
        relation.setJoinColumnens(joinColumnens);
    }


    public void initInverseJionClomns(Field field, Relation relation) {

        InverseJoinColumn joinColumn = field.getAnnotation(InverseJoinColumn.class);

        InverseJoinColumns joinColumns = field.getAnnotation(InverseJoinColumns.class);

        List<InverseJoinColumnen> joinColumnens = new ArrayList<>();
        if (joinColumn != null) {
            InverseJoinColumnen joinColumnen = new InverseJoinColumnen();
            joinColumnen.setName(joinColumn.name());
            joinColumnen.setReferencedColumnName(joinColumn.referencedColumnName());
            joinColumnens.add(joinColumnen);
        }
        if (joinColumns != null) {
            for (int i = 0; i < joinColumns.value().length; i++) {
                InverseJoinColumnen joinColumnen = new InverseJoinColumnen();
                joinColumnen.setName(joinColumns.value()[i].name());
                joinColumnen.setReferencedColumnName(joinColumns.value()[i].referencedColumnName());
                joinColumnens.add(joinColumnen);
            }
        }
        relation.setInverseJoinColumns(joinColumnens);

    }


}
